﻿using System;
using Validity.Rules.Decorators;

namespace Validity.Contexts
{
  public interface IConditionalValidationContext<TObject, TValue>
    : IMessageValidationContext<TObject, TValue>
  {
    IMessageValidationContext<TObject, TValue> If(Func<Boolean> predicate);
    IMessageValidationContext<TObject, TValue> If(Func<TValue, Boolean> predicate);
    IMessageValidationContext<TObject, TValue> If(Func<TObject, Boolean> predicate);
    IMessageValidationContext<TObject, TValue> If(Func<TObject, TValue, Boolean> predicate);
  }

  public class ConditionalValidationContext<TObject, TValue>
    : MessageValidationContext<TObject, TValue>, IConditionalValidationContext<TObject, TValue>
  {
    public ConditionalValidationContext()
    { }

    public ConditionalValidationContext(IValidationContext<TObject, TValue> context)
      : base(context)
    { }

    #region If

    public IMessageValidationContext<TObject, TValue> If(Func<Boolean> predicate)
    {
      Verify.NotNull(predicate);
      return If((obj, val) => predicate.Invoke());
    }

    public IMessageValidationContext<TObject, TValue> If(Func<TValue, Boolean> predicate)
    {
      Verify.NotNull(predicate);
      return If((obj, val) => predicate.Invoke(val));
    }

    public IMessageValidationContext<TObject, TValue> If(Func<TObject, Boolean> predicate)
    {
      Verify.NotNull(predicate);
      return If((obj, val) => predicate.Invoke(obj));
    }

    public IMessageValidationContext<TObject, TValue> If(Func<TObject, TValue, Boolean> predicate)
    {
      Verify.NotNull(predicate);

      DecorateLastRuleWith(rule => new ConditionalRuleDecorator<TObject, TValue>(rule, predicate));

      return new MessageValidationContext<TObject, TValue>(this);
    }

    #endregion

    #region Unless

    public IMessageValidationContext<TObject, TValue> Unless(Func<Boolean> predicate)
    {
      Verify.NotNull(predicate);
      return Unless((obj, val) => predicate.Invoke());
    }

    public IMessageValidationContext<TObject, TValue> Unless(Func<TValue, Boolean> predicate)
    {
      Verify.NotNull(predicate);
      return Unless((obj, val) => predicate.Invoke(val));
    }

    public IMessageValidationContext<TObject, TValue> Unless(Func<TObject, Boolean> predicate)
    {
      Verify.NotNull(predicate);
      return Unless((obj, val) => predicate.Invoke(obj));
    }

    public IMessageValidationContext<TObject, TValue> Unless(Func<TObject, TValue, Boolean> predicate)
    {
      Verify.NotNull(predicate);
      return If((obj, val) => !predicate.Invoke(obj, val));
    }

    #endregion
  }
}